package com.xbniao.gcfs.common.util.ExportExcel;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;

import com.xbniao.gcfs.common.ApiStatusCode;
import com.xbniao.gcfs.common.BaseException;
import com.xbniao.gcfs.common.request.model.GcfsEbayCustomOrderModel;
import com.xbniao.gcfs.common.request.model.GcfsEbayCustomOrderProductModel;
import com.xbniao.gcfs.common.util.DateSyncUtil;
import com.xbniao.gcfs.common.util.DateUtil;
import com.xbniao.gcfs.dto.GcfsEbayCustomOrderProduct;
import com.xbniao.gcfs.dto.GcfsEbayPackPlan;
import com.xbniao.gcfs.dto.GcfsEbayPackPlanCfOrder;
import com.xbniao.gcfs.dto.GcfsEbayPackPlanCfOrderBox;
import com.xbniao.gcfs.dto.GcfsEbayPackPlanCfOrderBoxProduct;

/**
 * @Author:wangbin.
 * @Description.
 * @Date:Created in 16:16 2018/5/3.
 * @Modified By:
 */
public class ExportCustomsInformation extends ExportExcelBase implements Runnable{
    private CyclicBarrier barrier = new CyclicBarrier(4,this);
    private volatile boolean flag = false;
    private ExecutorService executorService = Executors.newFixedThreadPool(4);

    @Override
    public void run() {
        // 标识整个excel处理完成
        flag = true;
        // 关闭线程池
        executorService.shutdown();
    }

    @Override
    public void read(Workbook wb, Object data) throws Exception {
        // 数据类型进行强制转换
        final ExportParam model = (ExportParam) data;
        // 处理数据
        read(wb,model);
        // 等待处理完成
        while (!flag)
            TimeUnit.MILLISECONDS.sleep(5);
    }

    /**
     * 多线程处理excel各个sheet
     * @param wb
     * @param model
     */
    private void read( Workbook wb, ExportParam model)
    {
        processSheet(wb.getSheetAt(0),model,0);
        processSheet(wb.getSheetAt(1),model,1);
        processSheet(wb.getSheetAt(2),model,2);
        processSheet(wb.getSheetAt(3),model,3);
    }

    /**
     * 处理第一个sheet
     * @param sheet
     * @param model
     */
    private void processSheet1(Sheet sheet, ExportParam model) {
        // 处理非产品数据
        dealSheet1BaseInfoCell(sheet,model);
        List<GcfsEbayCustomOrderProductModel> products =  model.getModel().getGcfsEbayCustomOrderProductModels();
        int row=13;
        int no=1;
        for(GcfsEbayCustomOrderProductModel product:products) {
        	POIExportExcelUtil.updateCellValue(sheet,row,0,no);
        	POIExportExcelUtil.updateCellValue(sheet,row,1,product.getMerchantSku());
        	POIExportExcelUtil.updateCellValue(sheet,row,3,product.getCustomNameChinese());
        	POIExportExcelUtil.updateCellValue(sheet,row,6,product.getCustomNameEnglish());
        	POIExportExcelUtil.updateCellValue(sheet,row,7,product.getSpecsModel());
        	POIExportExcelUtil.updateCellValue(sheet,row,9,product.getDealNumber());
        	POIExportExcelUtil.updateCellValue(sheet,row,12,product.getApplyPrice());
        	POIExportExcelUtil.updateCellValue(sheet,row,13,product.getTotalApplyPrice());
        	row++;
        	no++;
        }
        sheet.setForceFormulaRecalculation(true);
    }

    /**
     * 处理sheet1的非产品数据信息
     * @param sheet
     * @param model
     */
    private void dealSheet1BaseInfoCell(Sheet sheet, ExportParam model) {
    	Map<String,Object> data=model.getMap();
    	GcfsEbayCustomOrderModel customOrderModel=model.getModel();
        // 第五行   设置DATE：
        POIExportExcelUtil.updateCellValue(sheet,4,1, DateSyncUtil.formatDate_Slash(new Date(System.currentTimeMillis())));
        try {
			POIExportExcelUtil.updateCellValue(sheet,4,13, DateSyncUtil.formatDate_yyyymmdd(new Date())+StringUtils.right(customOrderModel.getCustomOrdersCode(),11));
		} catch (ParseException e) {
			e.printStackTrace();
		}
        GcfsEbayPackPlan plan = (GcfsEbayPackPlan) data.get(customOrderModel.getId()+"plan");
        POIExportExcelUtil.updateCellValue(sheet,8,10,customOrderModel.getTransportToolName());
        POIExportExcelUtil.updateCellValue(sheet,8,13,customOrderModel.getVoyage());
        POIExportExcelUtil.updateCellValue(sheet,9,2,DateSyncUtil.formatDate_Slash(plan.getShipPeriod()));
        POIExportExcelUtil.updateCellValue(sheet,9,6,customOrderModel.getStartPort()+" CHINA");
        POIExportExcelUtil.updateCellValue(sheet,9,12,customOrderModel.getFingerPort()+" "+customOrderModel.getDestinationCountry());
    }

    /**
     * 处理第二个sheet
     * @param sheet
     * @param model
     */
    private void processSheet2(Sheet sheet, ExportParam model) {
    	dealSheet2BaseInfoCell(sheet, model);
    	List<GcfsEbayCustomOrderProductModel> products = model.getModel().getGcfsEbayCustomOrderProductModels();
        int row=14;
        int no=1;
        for(GcfsEbayCustomOrderProductModel product:products) {
        	POIExportExcelUtil.updateCellValue(sheet,row,0,no);
        	POIExportExcelUtil.updateCellValue(sheet,row,1,product.getMerchantSku());
        	POIExportExcelUtil.updateCellValue(sheet,row,3,product.getCustomNameChinese());
        	POIExportExcelUtil.updateCellValue(sheet,row,6,product.getCustomNameEnglish());
        	POIExportExcelUtil.updateCellValue(sheet,row,7,product.getSpecsModel());
        	POIExportExcelUtil.updateCellValue(sheet,row,9,product.getDealNumber());
        	POIExportExcelUtil.updateCellValue(sheet,row,12,product.getApplyPrice());
        	POIExportExcelUtil.updateCellValue(sheet,row,13,product.getTotalApplyPrice());
        	row++;
        	no++;
        }
        POIExportExcelUtil.updateCellValue(sheet,57,1,model.getModel().getExchangeType());
        sheet.setForceFormulaRecalculation(true);
    }
    
    
    /**
     * 处理sheet2的非产品数据信息
     * @param sheet
     * @param model
     */
    private void dealSheet2BaseInfoCell(Sheet sheet, ExportParam model) {
    	Map<String,Object> data=model.getMap();
    	GcfsEbayCustomOrderModel customOrderModel=model.getModel();
        // 第五行   设置DATE：
        POIExportExcelUtil.updateCellValue(sheet,4,1, DateSyncUtil.formatDate_Slash(new Date(System.currentTimeMillis())));
        try {
			POIExportExcelUtil.updateCellValue(sheet,4,13, DateSyncUtil.formatDate_yyyymmdd(new Date())+StringUtils.right(customOrderModel.getCustomOrdersCode(),11));
		} catch (ParseException e) {
			e.printStackTrace();
		}
        POIExportExcelUtil.updateCellValue(sheet,10,2,customOrderModel.getStartPort()+" CHINA");
        POIExportExcelUtil.updateCellValue(sheet,10,6,customOrderModel.getStartPort()+" CHINA");
        POIExportExcelUtil.updateCellValue(sheet,10,12,customOrderModel.getFingerPort()+" "+customOrderModel.getDestinationCountry());
    }

    /**
     * 处理第三个sheet
     * @param sheet
     * @param model
     */
    private void processSheet3(Sheet sheet, ExportParam model) {
    	dealSheet3BaseInfoCell(sheet, model);
    	GcfsEbayCustomOrderModel customOrderModel = model.getModel();
    	Map<String,Object> data=model.getMap();
    	List<GcfsEbayPackPlanCfOrder> cfOrders= (List<GcfsEbayPackPlanCfOrder>) data.get(customOrderModel.getId()+"cfOrders");
    	int row=13;
    	int no=1;
    	for(GcfsEbayPackPlanCfOrder cf1:cfOrders) {
    		POIExportExcelUtil.updateCellValue(sheet, row, 0, no);
    		POIExportExcelUtil.updateCellValue(sheet, row, 1, cf1.getCfOrderCode());
    		for(GcfsEbayPackPlanCfOrder cf2:cfOrders) {
        		if(cf1.getCfOrderCode().equals(cf2.getCfOrderCode())) {
        			List<GcfsEbayPackPlanCfOrderBox> boxs=cf2.getPackPlanCfOrderBoxes();
        			for(GcfsEbayPackPlanCfOrderBox box:boxs) {
        				POIExportExcelUtil.updateCellValue(sheet, row, 2,box.getCfOrderBoxNum());
        				for(GcfsEbayPackPlanCfOrderBoxProduct product:box.getPackPlanCfOrderBoxProducts()) {
        					POIExportExcelUtil.updateCellValue(sheet, row, 3,product.getMerchantSku());
        					POIExportExcelUtil.updateCellValue(sheet, row, 4,product.getCustomNameChinese());
        					POIExportExcelUtil.updateCellValue(sheet, row, 7,product.getCustomNameEnglish());
        					POIExportExcelUtil.updateCellValue(sheet, row, 8,product.getSpecsModel());
        					POIExportExcelUtil.updateCellValue(sheet, row, 10,product.getSingleBoxProductNum());
        					POIExportExcelUtil.updateCellValue(sheet, row, 11,product.getSingleBoxProductNum()*box.getCfOrderBoxNum());
        					POIExportExcelUtil.updateCellValue(sheet, row, 13,product.getNetWeight());
        					POIExportExcelUtil.updateCellValue(sheet, row, 14,new BigDecimal(product.getSingleBoxProductNum()*box.getCfOrderBoxNum()).multiply(product.getNetWeight()));
        					//该序号对应的箱子的单个箱子的毛重（含箱子中的产品毛重）。
        					POIExportExcelUtil.updateCellValue(sheet, row, 15,box.getBoxWeight());
        					//该序号对应的箱子的所有箱子总毛重。
        					POIExportExcelUtil.updateCellValue(sheet, row, 16,new BigDecimal(box.getCfOrderBoxNum()).multiply(box.getBoxWeight()));
        					POIExportExcelUtil.updateCellValue(sheet, row, 17,box.getBoxVolume().multiply(new BigDecimal(box.getCfOrderBoxNum())));
        					row++;
        				}
        			}
        			
        		}
        	}
    		no++;
    	}
    	sheet.setForceFormulaRecalculation(true);
    }
    
    /**
     * 处理sheet2的非产品数据信息
     * @param sheet
     * @param model
     */
    private void dealSheet3BaseInfoCell(Sheet sheet, ExportParam model) {
    	GcfsEbayCustomOrderModel customOrderModel=model.getModel();
        // 第五行   设置DATE：
        POIExportExcelUtil.updateCellValue(sheet,4,1, DateSyncUtil.formatDate_Slash(new Date(System.currentTimeMillis())));
        try {
			POIExportExcelUtil.updateCellValue(sheet,4,14, DateSyncUtil.formatDate_yyyymmdd(new Date())+StringUtils.right(customOrderModel.getCustomOrdersCode(),11));
		} catch (ParseException e) {
			e.printStackTrace();
		}
        POIExportExcelUtil.updateCellValue(sheet,8,11,customOrderModel.getTransportToolName());
        POIExportExcelUtil.updateCellValue(sheet,8,14,customOrderModel.getVoyage());
        Map<String,Object> data=model.getMap();
        GcfsEbayPackPlan plan = (GcfsEbayPackPlan) data.get(customOrderModel.getId()+"plan");
        POIExportExcelUtil.updateCellValue(sheet,9,3,DateSyncUtil.formatDate_Slash(plan.getShipPeriod()));
        POIExportExcelUtil.updateCellValue(sheet,9,7,customOrderModel.getStartPort()+" CHINA");
        POIExportExcelUtil.updateCellValue(sheet,9,13,customOrderModel.getFingerPort()+" "+customOrderModel.getDestinationCountry());
    	
    }

    /**
     * 处理第四个sheet
     * @param sheet
     * @param model
     */
    private void processSheet4(Sheet sheet, ExportParam model) {
    	GcfsEbayCustomOrderModel customOrderModel=model.getModel();
    	List<GcfsEbayCustomOrderProductModel> products =  model.getModel().getGcfsEbayCustomOrderProductModels();
    	GcfsEbayCustomOrderProductModel product=products.get(0);
    	try {
			POIExportExcelUtil.updateCellValue(sheet,1,5, DateSyncUtil.formatDate_yyyymmdd(new Date())+StringUtils.right(customOrderModel.getCustomOrdersCode(),11));
			POIExportExcelUtil.updateCellValue(sheet,7,2,DateSyncUtil.formatDate_China(DateUtil.monthCount(new Date(),1)));
			String date=DateSyncUtil.formatDate_China(new Date());
			POIExportExcelUtil.updateCellValue(sheet,12,5,date);
			POIExportExcelUtil.updateCellValue(sheet,17,1,product.getCustomNameChinese());//主要货物名称
			POIExportExcelUtil.updateCellValue(sheet,18,1,product.getHsCode());//HS编码
			POIExportExcelUtil.updateCellValue(sheet,18,4,date);
			POIExportExcelUtil.updateCellValue(sheet,19,1,date);
			POIExportExcelUtil.updateCellValue(sheet,20,1,customOrderModel.getSalveWaybillCode());//提单号
			POIExportExcelUtil.updateCellValue(sheet,21,1,customOrderModel.getSupervision());//贸易方式
			POIExportExcelUtil.updateCellValue(sheet,22,1,customOrderModel.getDomesticSource());//原产地/货源地
			POIExportExcelUtil.updateCellValue(sheet,38,1,date);
			POIExportExcelUtil.updateCellValue(sheet,38,5,date);
		} catch (ParseException e) {
			e.printStackTrace();
		}
    	sheet.setForceFormulaRecalculation(true);
    }

    /*************************以下提供controller的工具类************************/

    /**
     * 线程等待
     * @param barrier
     */
    private void awaitBarrier(CyclicBarrier barrier)
    {
        try {
            barrier.await();
        } catch (Exception e) {
            e.printStackTrace();
            // 出现异常，关闭掉线程池
            executorService.shutdown();
            // 修改标记
            flag = true;
        }
    }

    /**
     * 处理sheet
     * @param sheet
     * @param model
     */
    private void processSheet(final Sheet sheet,final ExportParam model,final int i)
    {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                switch (i)
                {
                    case 0:
                        // 处理第一个sheet
                        processSheet1(sheet,model); break;
                    case 1:
                        // 处理第二个sheet
                        processSheet2(sheet,model); break;
                    case 2:
                        // 处理第三个sheet
                        processSheet3(sheet,model); break;
                    case 3:
                        // 处理第四个sheet
                        processSheet4(sheet,model); break;
                }
                // 等待其他sheet处理完
                awaitBarrier(barrier);
            }
        });
    }

    /**
     * 处理报关订单和装箱计划中cf订单信息对应关系
     * @param customOrders
     * @param plans
     */
    public static Map<String,Object> dealData(List<GcfsEbayCustomOrderModel> customOrders,
                                              List<GcfsEbayPackPlan> plans,
                                              List<GcfsEbayCustomOrderProduct> orderProducts) throws BaseException {
        Map<String,Object> resultData = new HashMap<>();
        // 分类，每个报关订单中所有cf订单及cf订单箱子序号对应
        for(GcfsEbayCustomOrderModel orderModel : customOrders)
        {
            String packPlanId = orderModel.getPackPlanId();
            if(StringUtils.isEmpty(packPlanId))
                continue;
            // 用于存放当前报关订单对应的所有cf订单号
            Set<String> orderCodeSet = new HashSet<>();
            // 用于存放当前报关订单对应的cf订单对应的箱子序号
            Set<String> boxNoSet = new HashSet<>();
            // 过滤属于当前报关订单的所有cf订单及箱子序号信息
            for(GcfsEbayCustomOrderProduct product : orderProducts)
            {
                if(StringUtils.isEmpty(product.getCustomOrdersId()))
                    continue;
                if(product.getCustomOrdersId().equals(orderModel.getId()))
                {
                    if(!StringUtils.isEmpty(product.getCustomOrdersCode()) &&
                            !StringUtils.isEmpty(product.getBoxNo()))
                    {
                        orderCodeSet.add(product.getCfOrderCode());
                        boxNoSet.add(product.getCfOrderCode() + "#" + product.getBoxNo());
                    }
                }
            }
            // 因为：报关订单中，对于一个装箱计划来说，一种货品必须全在一个报关单上；且一个箱子中的货品必须全在一个报关单上
            for(GcfsEbayPackPlan plan : plans)
            {
                if(plan == null)
                    throw new BaseException(ApiStatusCode.GCFS_EBAY_PACK_PLAN_INFO_IS_NULL.getCode());
                // 判断当前报关订单是否是由该装箱计划生成
                if(packPlanId.equals(plan.getId()))
                {
                    List<GcfsEbayPackPlanCfOrder> cfOrders = plan.getPackPlanCfOrders();
                    List<GcfsEbayPackPlanCfOrder> newCfOrders = new ArrayList<>();
                    for(GcfsEbayPackPlanCfOrder order : cfOrders)
                    {
                        // 判断当前cf订单是否在报关订单中
                        if(orderCodeSet.contains(order.getCfOrderCode()))
                        {
                            GcfsEbayPackPlanCfOrder newOrder = new GcfsEbayPackPlanCfOrder();
                            BeanUtils.copyProperties(order,newOrder);
                            List<GcfsEbayPackPlanCfOrderBox> newBoxes = new ArrayList<>();
                            List<GcfsEbayPackPlanCfOrderBox> boxes = order.getPackPlanCfOrderBoxes();
                            for(GcfsEbayPackPlanCfOrderBox box : boxes)
                            {
                                // 判断当前cf订单中箱子序号是否在当前的报关订单中
                                if(boxNoSet.contains(box.getCfOrderCode() + "#" + box.getCfOrderBoxNo()))
                                    newBoxes.add(box);
                            }
                            if(CollectionUtils.isEmpty(newBoxes))
                                throw new BaseException(ApiStatusCode.GCFS_EBAY_CUSTOM_ORDER_PACK_PLAN_BOX_INFO_IS_NULL.getCode());
                            newOrder.setPackPlanCfOrderBoxes(newBoxes);
                            newCfOrders.add(newOrder);
                        }
                    }
                    if(CollectionUtils.isEmpty(newCfOrders))
                        throw new BaseException(ApiStatusCode.GCFS_EBAY_CUSTOM_ORDER_PACK_PLAN_CF_ORDER_INFO_IS_NULL.getCode());
                    // 存放当前报关订单的cf订单信息（处理 箱单 sheet用）
                    resultData.put(orderModel.getId() + "cfOrders", newCfOrders);
                    // 存放当前报关订单对应的装箱计划信息（有一些信息需要用到报关订单对应的装箱计划的信息）
                    resultData.put(orderModel.getId() + "plan", plan);
                    // 由于一个报关订单只能由一个装箱计划生成，找到与之对应的装箱计划后，没有必要再循环下面的装箱计划
                    break;
                }
            }
        }
        return resultData;
    }

    public static ExportParam createExportParam(GcfsEbayCustomOrderModel model,Map<String, Object> map) throws BaseException
    {
        return new ExportParam(model,map);
    }

    public static class ExportParam{
        // 报关订单信息
        private GcfsEbayCustomOrderModel model;
        // key:报关订单id+“cfOrders” value:当前报关订单的cf订单信息
        // key:报关订单id+“plan” value:当前报关订单对应的装箱计划信息
        private Map<String, Object> map;

        public ExportParam(GcfsEbayCustomOrderModel model, Map<String, Object> map) throws BaseException{
            if(model == null || map == null)
                throw new BaseException("报关订单信息和报关订单相关联的装箱计划信息不能为空！");
            this.model = model;
            this.map = map;
        }

        public GcfsEbayCustomOrderModel getModel() {
            return model;
        }

        public void setModel(GcfsEbayCustomOrderModel model) {
            this.model = model;
        }

        public Map<String, Object> getMap() {
            return map;
        }

        public void setMap(Map<String, Object> map) {
            this.map = map;
        }
    }
}
